#ifndef SERVERSYNCREQUEST_H_
#define SERVERSYNCREQUEST_H_

#include "scatu/ServerRequests.h"

namespace scatu {

template<TServerFunction Function, typename ReturnType, typename ArgsType>
class RSynchronousRequest<EServer, Function, ReturnType, ArgsType> : public RServerRequestBase
    {
    friend class RServerRequestArray;
    
private:
    
    typedef typename ReturnType::ReturnType IntegralReturnType;
    
    // Needed for run time defined functions.
    struct Callable
        {
        virtual IntegralReturnType CallL(ArgsType const & aArgs) = 0;
        };
    
    template<typename ServerType>
    class TServeFunction : public Callable
        {
    public:
        typedef typename ArgsType::template MemFunWithArgs<ServerType, IntegralReturnType> MemFun;
        typedef typename MemFun::FuncPtr FuncPtr;
        
        TServeFunction()
          : iServer(NULL), iServeFunction(NULL) {}
        TServeFunction(ServerType & aServer, FuncPtr aFuncPtr)
          : iServer(&aServer), iServeFunction(aFuncPtr) {}
        
        IntegralReturnType CallL(ArgsType const & aArgs)
            { return MemFun::CallL(*iServer, iServeFunction, aArgs); }
        
    private:
        ServerType * iServer;
        FuncPtr iServeFunction;
        };

public:
    RSynchronousRequest()
      : RServerRequestBase(Function)
      , iServerFunction(NULL)
      , iReturnBuffer(NULL)
        {}
    
    template<typename ServerType>
    void SetServeFunctionL(ServerType & aServer, typename TServeFunction<ServerType>::FuncPtr aFuncPtr)
        {
        delete iServerFunction;
        iServerFunction = NULL;
        iServerFunction = new (ELeave) TServeFunction<ServerType>(aServer, aFuncPtr);
        }
    
    void UnregisterServeFunctionL() { delete iServerFunction; }
    
protected: // RRequestBase implementation
    void Close()
        {
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        delete iServerFunction;
        iServerFunction = NULL;
        }
    
    virtual TBool IsPending() { return EFalse; } // Synchronous => never pending
    virtual TBool ReadyToReturn() { return iReturnBuffer != NULL; }
    
    virtual void ReceiveDataL(RMessage2 const & aMessage)
        {
        // Make sure no data is left over from previous calls
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        
        HBufC8 * argBuf = ReadArgumentsLC(aMessage);
        ArgsType argumentData;
        CleanupClosePushL(argumentData);
        argumentData.DeserializeL(*argBuf);
        
        CallFunctor<ReturnType::IsAssignable, void> functor;
        TRAPD(err, functor.CallL(*iServerFunction, iReturnBuffer, aMessage, argumentData));
        
        CleanupStack::PopAndDestroy(2, argBuf);
        aMessage.Complete(err);
        }
    
    virtual void ReturnDataL(RMessage2 const & aMessage)
        {
        aMessage.WriteL(EMessageSlotData, *iReturnBuffer);
        aMessage.Complete(KErrNone);
        
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        }
    
private:
    
    // Conditional template based on assignability
    // This has to be a type for specialization to work
    // UnusedType is to work around explicit template specialization rules
    // Not the beautiful side of templates, is it...
    template<TBool IsAssignable, typename UnusedType>
    class CallFunctor { };
    
    template<typename UnusedType>
    class CallFunctor<ETrue, UnusedType>
    {
        // WINSCW name lookup bug workaround
        typedef typename RSynchronousRequest<EServer, Function, ReturnType, ArgsType>::Callable Callable;
        
    public:
        void CallL(Callable & aServerFunction, HBufC8 *& aReturnBuffer,
                RMessage2 const & aMessage, ArgsType const & aArgs)
            {
            ReturnType returnData(aServerFunction.CallL(aArgs), EReturnTypeDesignator);
            CleanupClosePushL(returnData);
            aReturnBuffer = returnData.SerializeL();
            TPckg<TInt> sizeBuf(aReturnBuffer->Size());
            aMessage.WriteL(EMessageSlotBufSize, sizeBuf);
            CleanupStack::PopAndDestroy(&returnData);
            }
    };
    
    template<typename UnusedType>
    class CallFunctor<EFalse, UnusedType>
    {
    public:
        void CallL(Callable & aServerFunction, HBufC8 *&,
                RMessage2 const &, ArgsType const & aArgs)
            {
            aServerFunction.CallL(aArgs);
            }
    };
    
private:
    Callable * iServerFunction;
    HBufC8 * iReturnBuffer;
    };


} // namespace scatu

#endif /* SERVERSYNCREQUEST_H_ */
